﻿#if USING_TIMELINE

using Devil;
using GameToolkit.UI;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

namespace GameToolkit
{
    [TrackColor(0.1f, 0.6f, 1f)]
    [TrackClipType(typeof(PanelClip))]
    [TrackBindingType(typeof(SignalReceiver))]
    public class PanelTrack : TrackAsset
    {
        [SerializeField, AssetPath(typeof(GameObject))]
        AssetAddress m_KeepPanelAlive;

        public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
        {
            var play = ScriptPlayable<PanelTrackBehaviour>.Create(graph, inputCount);// base.CreateTrackMixer(graph, go, inputCount);
            var behaviour = play.GetBehaviour();
            behaviour.mKeepPanelAlive = m_KeepPanelAlive.ToString();
            behaviour.mParent = go;
#if UNITY_EDITOR
            foreach(var clip in m_Clips)
            {
                if (clip.asset != null)
                    clip.displayName = clip.asset.name;
            }
#endif
            return play;
        }

        public class PanelTrackBehaviour : PlayableBehaviour
        {
            internal string mKeepPanelAlive;
            internal GameObject mParent;
            Dictionary<int, PanelReference> mPanelRefs = new Dictionary<int, PanelReference>();

            public PanelReference GetReference(string ui)
            {
                var id = ui.IgnoreCaseToHash();
                PanelReference refins;
                if (mPanelRefs.TryGetValue(id, out refins))
                    return refins;
                var pref = new PanelReference(mParent, ui);
                mPanelRefs.Add(id, pref);
                return pref;
            }

            PanelReference mRef;

            public override void OnPlayableCreate(Playable playable)
            {
                if (PanelManager.Instance != null && !string.IsNullOrWhiteSpace(mKeepPanelAlive))
                    mKeepPanelAlive = PanelManager.Instance.GetRealUri(mKeepPanelAlive);
            }

            public override void OnGraphStart(Playable playable)
            {
                if (!string.IsNullOrEmpty(mKeepPanelAlive))
                {
                    mRef = GetReference(mKeepPanelAlive);
                    mRef.Use(this);
                }
            }

            public override void OnGraphStop(Playable playable)
            {
                if(mRef != null)
                {
                    mRef.Unuse(this);
                    mRef = null;
                }
            }
        }

        public class PanelReference
        {
            GameObject mParent;
            string mPath;
            HashSet<object> mRefs = new HashSet<object>();
            public bool IsUsing => mRefs.Count > 0;
            Panel mPanel;

#if UNITY_EDITOR
            GameObject mPreview;
#endif

            public PanelReference(GameObject parent, string ui)
            {
                mParent = parent;
                mPath = ui;
            }

            public bool Use(object user)
            {
                if (user != null && mRefs.Add(user))
                {
                    var mgr = PanelManager.Instance;
                    if (mgr != null && mRefs.Count == 1)
                        mgr.OnClosePanel += OnPanelClosed;
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public void Unuse(object user)
            {
                if (user == null || !mRefs.Remove(user))
                    return;
#if UNITY_EDITOR
                if (!Application.isPlaying)
                    Cleanup();
                else
#endif
                    MainThread.RunOnMainThread(Cleanup);
            }

            public void Cleanup()
            {
                if (mRefs.Count == 0)
                {
                    OnPanelClosed(null);
                    var mgr = PanelManager.Instance;
                    if (mgr != null)
                    {
                        mgr.OnClosePanel -= OnPanelClosed;
                        PanelManager.Instance.ClosePanel(mPath);
                    }
#if UNITY_EDITOR
                    if (mPreview != null)
                        ParallelUnity.DestroyAutomatic(mPreview);
                    mPreview = null;
#endif
                }
            }

#if UNITY_EDITOR
            void OpenPreview()
            {
                if (!Application.isPlaying && mParent != null)
                {
                    if (mPreview == null)
                    {
                        var asset = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(mPath);
                        if (asset != null)
                        {
                            mPreview = Instantiate(asset, mParent.transform);
                            mPreview.name = $"Preview ({NativeFileSystem.GetFileName(mPath, true)})";
                            mPreview.hideFlags |= HideFlags.DontSave;
                        }
                    }
                    if (mPreview != null)
                    {
                        mPreview.SetActive(true);
                    }
                }
            }
#endif

            public void Open(object user)
            {
                if (!Use(user))
                    return;
                if (PanelManager.Instance != null)
                {
                    PanelManager.Instance.OpenPanel(mPath, OnPanelOpend);
                }
#if UNITY_EDITOR
                else 
                {
                    OpenPreview();
                }
#endif
            }

            public void Open<T>(object user, T intent)
            {
                if (!Use(user))
                    return;
                if (PanelManager.Instance != null)
                {
                    PanelManager.Instance.OpenPanel(mPath, intent, OnPanelOpend);
                }
#if UNITY_EDITOR
                else
                {
                    OpenPreview();
                    if (mPreview != null)
                    {
                        var handler = mPreview.GetComponent<IPanelIntentHandler<T>>();
                        if (handler != null)
                            handler.OnHandleIntent(intent);
                    }
                }
#endif
            }

            public void Terminate<T>(T intent)
            {
                if(PanelManager.Instance != null)
                {
                    PanelManager.Instance.Terminate(mPath, intent);
                }
#if UNITY_EDITOR
                else if(mPreview != null)
                {
                    var handler = mPreview.GetComponent<IPanelIntentTerminate<T>>();
                    if (handler != null)
                        handler.OnTerminateIntent(intent);
                }
#endif
            }

            private void OnPanelOpend(string uri, bool isOpened, Panel panel)
            {
                if(!isOpened)
                {
                    OnPanelClosed(null);
                }
                else
                {
                    mPanel = panel;
                }
            }

            void OnPanelClosed(Panel panel)
            {
                if (panel == null || panel == mPanel)
                {
                    mPanel = null;
                    var lst = CachedList<PanelClip.PanelBehaviour>.GetList();
                    foreach (var src in mRefs)
                    {
                        if (src is PanelClip.PanelBehaviour pb)
                            lst.Add(pb);
                    }
                    for (int i = 0; i < lst.Count; i++)
                    {
                        lst[i].OnPanelClosed();
                    }
                    CachedList<PanelClip.PanelBehaviour>.Release(lst);
                }
            }

        }
    }
}

#endif